<!doctype html>
<html>

<head>
    <meta charset="utf-8">
    <title>❤永远开心❤</title>

    <style>
        html,
        body {
            margin: 0px;
            width: 100%;
            height: 100%;
            overflow: hidden;
            background: #000;
        }

        #canvas {
            width: 100%;
            height: 100%;
        }

        html,
        body {
            height: 100%;
            padding: 0;
            margin: 0;
            background: #000;
        }

        canvas {
            position: absolute;
            width: 100%;
            height: 100%;
        }

        #child {
            position: absolute;
            left: 50%;
            top: 50%;
            transform: translate(-50%, -50%);

        }

        h4 {
            font-family: "STKaiti";
         
         
         
            font-size: 40px;
            color: #f584b7;
            position: relative;
        }
    </style>
</head>

<body>
    <div id="child">
        <h4>💗李嘉鑫 and 江紫薏💗</h4>
    </div><!--这里写名字❤！！！-->
    <canvas id="pinkboard"></canvas>
    <canvas id="canvas"></canvas>

    <script type="text/javascript">
        var settings = {
          particles: {
            length: 500, //最大粒子数
            duration: 2, //粒子持续时间
            velocity: 50, //粒子速度，像素/秒
            effect: -0.75,
            size: 30, //粒子大小
          }
        };
        (function() {
          var b = 0;
          var c = ["ms", "moz", "webkit", "o"];
          for (var a = 0; a < c.length && !window.requestAnimationFrame; ++a) {
            window.requestAnimationFrame = window[c[a] + "RequestAnimationFrame"];
            window.cancelAnimationFrame = window[c[a] + "CancelAnimationFrame"] || window[c[a] + "CancelRequestAnimationFrame"]
          }
          if (!window.requestAnimationFrame) {
            window.requestAnimationFrame = function(h, e) {
              var d = new Date().getTime();
              var f = Math.max(0, 16 - (d - b));
              var g = window.setTimeout(function() {
                h(d + f)
              }, f);
              b = d + f;
              return g
            }
          }
          if (!window.cancelAnimationFrame) {
            window.cancelAnimationFrame = function(d) {
              clearTimeout(d)
            }
          }
        }());
       
        var Point = (function() {
          function Point(x, y) {
            this.x = (typeof x !== 'undefined') ? x : 0;
            this.y = (typeof y !== 'undefined') ? y : 0;
          }
          Point.prototype.clone = function() {
            return new Point(this.x, this.y);
          };
          Point.prototype.length = function(length) {
            if (typeof length == 'undefined')
              return Math.sqrt(this.x * this.x + this.y * this.y);
            this.normalize();
            this.x *= length;
            this.y *= length;
            return this;
          };
          Point.prototype.normalize = function() {
            var length = this.length();
            this.x /= length;
            this.y /= length;
            return this;
          };
          return Point;
        })();
        var Particle = (function() {
          function Particle() {
            this.position = new Point();
            this.velocity = new Point();
            this.acceleration = new Point();
            this.age = 0;
          }
          Particle.prototype.initialize = function(x, y, dx, dy) {
            this.position.x = x;
            this.position.y = y;
            this.velocity.x = dx;
            this.velocity.y = dy;
            this.acceleration.x = dx * settings.particles.effect;
            this.acceleration.y = dy * settings.particles.effect;
            this.age = 0;
          };
          Particle.prototype.update = function(deltaTime) {
            this.position.x += this.velocity.x * deltaTime;
            this.position.y += this.velocity.y * deltaTime;
            this.velocity.x += this.acceleration.x * deltaTime;
            this.velocity.y += this.acceleration.y * deltaTime;
            this.age += deltaTime;
          };
          Particle.prototype.draw = function(context, image) {
            function ease(t) {
              return (--t) * t * t + 1;
            }
            var size = image.width * ease(this.age / settings.particles.duration);
            context.globalAlpha = 1 - this.age / settings.particles.duration;
            context.drawImage(image, this.position.x - size / 2, this.position.y - size / 2, size, size);
          };
          return Particle;
        })();
        var ParticlePool = (function() {
          var particles,
                  firstActive = 0,
                  firstFree = 0,
                  duration = settings.particles.duration;
       
          function ParticlePool(length) {
            // 创建和填充粒子库
            particles = new Array(length);
            for (var i = 0; i < particles.length; i++)
              particles[i] = new Particle();
          }
          ParticlePool.prototype.add = function(x, y, dx, dy) {
            particles[firstFree].initialize(x, y, dx, dy);
       
            // 处理循环队列
            firstFree++;
            if (firstFree == particles.length) firstFree = 0;
            if (firstActive == firstFree) firstActive++;
            if (firstActive == particles.length) firstActive = 0;
          };
          ParticlePool.prototype.update = function(deltaTime) {
            var i;
       
            // 更新活动粒子
            if (firstActive < firstFree) {
              for (i = firstActive; i < firstFree; i++)
                particles[i].update(deltaTime);
            }
            if (firstFree < firstActive) {
              for (i = firstActive; i < particles.length; i++)
                particles[i].update(deltaTime);
              for (i = 0; i < firstFree; i++)
                particles[i].update(deltaTime);
            }
       
            // 删除不活动的粒子
            while (particles[firstActive].age >= duration && firstActive != firstFree) {
              firstActive++;
              if (firstActive == particles.length) firstActive = 0;
            }
       
       
          };
          ParticlePool.prototype.draw = function(context, image) {
            // 画活动粒子
            if (firstActive < firstFree) {
              for (i = firstActive; i < firstFree; i++)
                particles[i].draw(context, image);
            }
            if (firstFree < firstActive) {
              for (i = firstActive; i < particles.length; i++)
                particles[i].draw(context, image);
              for (i = 0; i < firstFree; i++)
                particles[i].draw(context, image);
            }
          };
          return ParticlePool;
        })();
        (function(canvas) {
          var context = canvas.getContext('2d'),
                  particles = new ParticlePool(settings.particles.length),
                  particleRate = settings.particles.length / settings.particles.duration, // particles/sec
                  time;
       
          // 获取heart点位 -PI <= t <= PI
          function pointOnHeart(t) {
            return new Point(
                    160 * Math.pow(Math.sin(t), 3),
                    130 * Math.cos(t) - 50 * Math.cos(2 * t) - 20 * Math.cos(3 * t) - 10 * Math.cos(4 * t) + 25
            );
          }
       
          // 使用canvas创建粒子图像
          var image = (function() {
            var canvas = document.createElement('canvas'),
                    context = canvas.getContext('2d');
            canvas.width = settings.particles.size;
            canvas.height = settings.particles.size;
            // 创建路径的辅助函数
            function to(t) {
              var point = pointOnHeart(t);
              point.x = settings.particles.size / 2 + point.x * settings.particles.size / 350;
              point.y = settings.particles.size / 2 - point.y * settings.particles.size / 350;
              return point;
            }
            // 创建路径
            context.beginPath();
            var t = -Math.PI;
            var point = to(t);
            context.moveTo(point.x, point.y);
            while (t < Math.PI) {
              t += 0.01;
              point = to(t);
              context.lineTo(point.x, point.y);
            }
            context.closePath();
            // 填充
            context.fillStyle = '#ea80b0';
            context.fill();
            // 创建图片
            var image = new Image();
            image.src = canvas.toDataURL();
            return image;
          })();
       
          // 渲染
          function render() {
            requestAnimationFrame(render);
       
            // update time
            var newTime = new Date().getTime() / 1000,
                    deltaTime = newTime - (time || newTime);
            time = newTime;
       
            // clear canvas
            context.clearRect(0, 0, canvas.width, canvas.height);
       
            // 创建新的粒子
            var amount = particleRate * deltaTime;
            for (var i = 0; i < amount; i++) {
              var pos = pointOnHeart(Math.PI - 2 * Math.PI * Math.random());
              var dir = pos.clone().length(settings.particles.velocity);
              particles.add(canvas.width / 2 + pos.x, canvas.height / 2 - pos.y, dir.x, -dir.y);
            }
       
            // 更新并绘制
            particles.update(deltaTime);
            particles.draw(context, image);
          }
       
          // 处理canvas大小
          function onResize() {
            canvas.width = canvas.clientWidth;
            canvas.height = canvas.clientHeight;
          }
          window.onresize = onResize;
       
          // 延迟渲染
          setTimeout(function() {
            onResize();
            render();
          }, 10);
        })(document.getElementById('pinkboard'));
      </script>

    <script>
        function initVars() {

            pi = Math.PI;
            ctx = canvas.getContext("2d");
            canvas.width = canvas.clientWidth;
            canvas.height = canvas.clientHeight;
            cx = canvas.width / 2;
            cy = canvas.height / 2;
            playerZ = -25;
            playerX = playerY = playerVX = playerVY = playerVZ = pitch = yaw = pitchV = yawV = 0;
            scale = 600;
            seedTimer = 0;
            seedInterval = 5, seedLife = 100;
            gravity = .02;
            seeds = new Array();
            sparkPics = new Array();
            s = "https://cantelope.org/NYE/";
            for (i = 1; i <= 10; ++i) {
                sparkPic = new Image();
                sparkPic.src = s + "spark" + i + ".png";
                sparkPics.push(sparkPic);
            }
            sparks = new Array();
            pow1 = new Audio(s + "pow1.ogg");
            pow2 = new Audio(s + "pow2.ogg");
            pow3 = new Audio(s + "pow3.ogg");
            pow4 = new Audio(s + "pow4.ogg");
            frames = 0;
        }

        function rasterizePoint(x, y, z) {

            var p, d;
            x -= playerX;
            y -= playerY;
            z -= playerZ;
            p = Math.atan2(x, z);
            d = Math.sqrt(x * x + z * z);
            x = Math.sin(p - yaw) * d;
            z = Math.cos(p - yaw) * d;
            p = Math.atan2(y, z);
            d = Math.sqrt(y * y + z * z);
            y = Math.sin(p - pitch) * d;
            z = Math.cos(p - pitch) * d;
            var rx1 = -1000, ry1 = 1, rx2 = 1000, ry2 = 1, rx3 = 0, ry3 = 0, rx4 = x, ry4 = z,
                uc = (ry4 - ry3) * (rx2 - rx1) - (rx4 - rx3) * (ry2 - ry1);
            if (!uc) return { x: 0, y: 0, d: -1 };
            var ua = ((rx4 - rx3) * (ry1 - ry3) - (ry4 - ry3) * (rx1 - rx3)) / uc;
            var ub = ((rx2 - rx1) * (ry1 - ry3) - (ry2 - ry1) * (rx1 - rx3)) / uc;
            if (!z) z = .000000001;
            if (ua > 0 && ua < 1 && ub > 0 && ub < 1) {
                return {
                    x: cx + (rx1 + ua * (rx2 - rx1)) * scale,
                    y: cy + y / z * scale,
                    d: Math.sqrt(x * x + y * y + z * z)
                };
            } else {
                return {
                    x: cx + (rx1 + ua * (rx2 - rx1)) * scale,
                    y: cy + y / z * scale,
                    d: -1
                };
            }
        }

        function spawnSeed() {

            seed = new Object();
            seed.x = -50 + Math.random() * 100;
            seed.y = 25;
            seed.z = -50 + Math.random() * 100;
            seed.vx = .1 - Math.random() * .2;
            seed.vy = -1.5;
            seed.vz = .1 - Math.random() * .2;
            seed.born = frames;
            seeds.push(seed);
        }

        function splode(x, y, z) {

            t = 5 + parseInt(Math.random() * 150);
            sparkV = 1 + Math.random() * 2.5;
            type = parseInt(Math.random() * 3);
            switch (type) {
                case 0:
                    pic1 = parseInt(Math.random() * 10);
                    break;
                case 1:
                    pic1 = parseInt(Math.random() * 10);
                    do {
                        pic2 = parseInt(Math.random() * 10);
                    } while (pic2 == pic1);
                    break;
                case 2:
                    pic1 = parseInt(Math.random() * 10);
                    do {
                        pic2 = parseInt(Math.random() * 10);
                    } while (pic2 == pic1);
                    do {
                        pic3 = parseInt(Math.random() * 10);
                    } while (pic3 == pic1 || pic3 == pic2);
                    break;
            }
            for (m = 1; m < t; ++m) {
                spark = new Object();
                spark.x = x;
                spark.y = y;
                spark.z = z;
                p1 = pi * 2 * Math.random();
                p2 = pi * Math.random();
                v = sparkV * (1 + Math.random() / 6)
                spark.vx = Math.sin(p1) * Math.sin(p2) * v;
                spark.vz = Math.cos(p1) * Math.sin(p2) * v;
                spark.vy = Math.cos(p2) * v;
                switch (type) {
                    case 0:
                        spark.img = sparkPics[pic1];
                        break;
                    case 1:
                        spark.img = sparkPics[parseInt(Math.random() * 2) ? pic1 : pic2];
                        break;
                    case 2:
                        switch (parseInt(Math.random() * 3)) {
                            case 0:
                                spark.img = sparkPics[pic1];
                                break;
                            case 1:
                                spark.img = sparkPics[pic2];
                                break;
                            case 2:
                                spark.img = sparkPics[pic3];
                                break;
                        }
                        break;
                }
                spark.radius = 25 + Math.random() * 50;
                spark.alpha = 1;
                spark.trail = new Array();
                sparks.push(spark);
            }
            switch (parseInt(Math.random() * 4)) {
                case 0:
                    pow = new Audio(s + "pow1.ogg");
                    break;
                case 1:
                    pow = new Audio(s + "pow2.ogg");
                    break;
                case 2:
                    pow = new Audio(s + "pow3.ogg");
                    break;
                case 3:
                    pow = new Audio(s + "pow4.ogg");
                    break;
            }
            d = Math.sqrt((x - playerX) * (x - playerX) + (y - playerY) * (y - playerY) + (z - playerZ) * (z - playerZ));
            pow.volume = 1.5 / (1 + d / 10);
            pow.play();
        }

        function doLogic() {

            if (seedTimer < frames) {
                seedTimer = frames + seedInterval * Math.random() * 10;
                spawnSeed();
            }
            for (i = 0; i < seeds.length; ++i) {
                seeds[i].vy += gravity;
                seeds[i].x += seeds[i].vx;
                seeds[i].y += seeds[i].vy;
                seeds[i].z += seeds[i].vz;
                if (frames - seeds[i].born > seedLife) {
                    splode(seeds[i].x, seeds[i].y, seeds[i].z);
                    seeds.splice(i, 1);
                }
            }
            for (i = 0; i < sparks.length; ++i) {
                if (sparks[i].alpha > 0 && sparks[i].radius > 5) {
                    sparks[i].alpha -= .01;
                    sparks[i].radius /= 1.02;
                    sparks[i].vy += gravity;
                    point = new Object();
                    point.x = sparks[i].x;
                    point.y = sparks[i].y;
                    point.z = sparks[i].z;
                    if (sparks[i].trail.length) {
                        x = sparks[i].trail[sparks[i].trail.length - 1].x;
                        y = sparks[i].trail[sparks[i].trail.length - 1].y;
                        z = sparks[i].trail[sparks[i].trail.length - 1].z;
                        d = ((point.x - x) * (point.x - x) + (point.y - y) * (point.y - y) + (point.z - z) * (point.z - z));
                        if (d > 9) {
                            sparks[i].trail.push(point);
                        }
                    } else {
                        sparks[i].trail.push(point);
                    }
                    if (sparks[i].trail.length > 5) sparks[i].trail.splice(0, 1);
                    sparks[i].x += sparks[i].vx;
                    sparks[i].y += sparks[i].vy;
                    sparks[i].z += sparks[i].vz;
                    sparks[i].vx /= 1.075;
                    sparks[i].vy /= 1.075;
                    sparks[i].vz /= 1.075;
                } else {
                    sparks.splice(i, 1);
                }
            }
            p = Math.atan2(playerX, playerZ);
            d = Math.sqrt(playerX * playerX + playerZ * playerZ);
            d += Math.sin(frames / 80) / 1.25;
            t = Math.sin(frames / 200) / 40;
            playerX = Math.sin(p + t) * d;
            playerZ = Math.cos(p + t) * d;
            yaw = pi + p + t;
        }

        function rgb(col) {

            var r = parseInt((.5 + Math.sin(col) * .5) * 16);
            var g = parseInt((.5 + Math.cos(col) * .5) * 16);
            var b = parseInt((.5 - Math.sin(col) * .5) * 16);
            return "#" + r.toString(16) + g.toString(16) + b.toString(16);
        }

        function draw() {

            ctx.clearRect(0, 0, cx * 2, cy * 2);

            ctx.fillStyle = "#ff8";
            for (i = -100; i < 100; i += 3) {
                for (j = -100; j < 100; j += 4) {
                    x = i;
                    z = j;
                    y = 25;
                    point = rasterizePoint(x, y, z);
                    if (point.d != -1) {
                        size = 250 / (1 + point.d);
                        d = Math.sqrt(x * x + z * z);
                        a = 0.75 - Math.pow(d / 100, 6) * 0.75;
                        if (a > 0) {
                            ctx.globalAlpha = a;
                            ctx.fillRect(point.x - size / 2, point.y - size / 2, size, size);
                        }
                    }
                }
            }
            ctx.globalAlpha = 1;
            for (i = 0; i < seeds.length; ++i) {
                point = rasterizePoint(seeds[i].x, seeds[i].y, seeds[i].z);
                if (point.d != -1) {
                    size = 200 / (1 + point.d);
                    ctx.fillRect(point.x - size / 2, point.y - size / 2, size, size);
                }
            }
            point1 = new Object();
            for (i = 0; i < sparks.length; ++i) {
                point = rasterizePoint(sparks[i].x, sparks[i].y, sparks[i].z);
                if (point.d != -1) {
                    size = sparks[i].radius * 200 / (1 + point.d);
                    if (sparks[i].alpha < 0) sparks[i].alpha = 0;
                    if (sparks[i].trail.length) {
                        point1.x = point.x;
                        point1.y = point.y;
                        switch (sparks[i].img) {
                            case sparkPics[0]:
                                ctx.strokeStyle = "#f84";
                                break;
                            case sparkPics[1]:
                                ctx.strokeStyle = "#84f";
                                break;
                            case sparkPics[2]:
                                ctx.strokeStyle = "#8ff";
                                break;
                            case sparkPics[3]:
                                ctx.strokeStyle = "#fff";
                                break;
                            case sparkPics[4]:
                                ctx.strokeStyle = "#4f8";
                                break;
                            case sparkPics[5]:
                                ctx.strokeStyle = "#f44";
                                break;
                            case sparkPics[6]:
                                ctx.strokeStyle = "#f84";
                                break;
                            case sparkPics[7]:
                                ctx.strokeStyle = "#84f";
                                break;
                            case sparkPics[8]:
                                ctx.strokeStyle = "#fff";
                                break;
                            case sparkPics[9]:
                                ctx.strokeStyle = "#44f";
                                break;
                        }
                        for (j = sparks[i].trail.length - 1; j >= 0; --j) {
                            point2 = rasterizePoint(sparks[i].trail[j].x, sparks[i].trail[j].y, sparks[i].trail[j].z);
                            if (point2.d != -1) {
                                ctx.globalAlpha = j / sparks[i].trail.length * sparks[i].alpha / 2;
                                ctx.beginPath();
                                ctx.moveTo(point1.x, point1.y);
                                ctx.lineWidth = 1 + sparks[i].radius * 10 / (sparks[i].trail.length - j) / (1 + point2.d);
                                ctx.lineTo(point2.x, point2.y);
                                ctx.stroke();
                                point1.x = point2.x;
                                point1.y = point2.y;
                            }
                        }
                    }
                    ctx.globalAlpha = sparks[i].alpha;
                }
            }
        }

        function frame() {

            if (frames > 100000) {
                seedTimer = 0;
                frames = 0;
            }
            frames++;
            draw();
            doLogic();
            requestAnimationFrame(frame);
        }

        window.addEventListener("resize", () => {
            canvas.width = canvas.clientWidth;
            canvas.height = canvas.clientHeight;
            cx = canvas.width / 2;
            cy = canvas.height / 2;
        });

        initVars();
        frame();

    </script>


</body>